home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Frameworks / TransSkel 3.18 / Source / TransSkel.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-04  |  60.4 KB  |  2,337 lines  |  [TEXT/KAHL]

  1. /*
  2.  * TransSkel - Transportable Macintosh application skeleton
  3.  * Release 3.18
  4.  *
  5.  * Please report problems to Paul DuBois.
  6.  *
  7.  *
  8.  * TransSkel is in the public domain and was originally written by:
  9.  *
  10.  *             Paul DuBois
  11.  *             Wisconsin Regional Primate Research Center
  12.  *             1220 Capitol Court
  13.  *             Madison, WI  53715-1299  USA
  14.  *
  15.  * Internet:    dubois@primate.wisc.edu
  16.  *            
  17.  * Additional changes were made by:
  18.  *    
  19.  *             Owen Hartnett
  20.  *             OHM Software Company
  21.  *             163 Richard Drive
  22.  *             Tiverton, RI 02878  USA
  23.  *         
  24.  * Internet:    omh@cs.brown.edu
  25.  * UUCP:        uunet!brunix!omh    
  26.  *
  27.  * Owen is also responsible for the port to THINK Pascal.
  28.  *
  29.  * Bob Schumaker joined the cast between versions 2.0 and 3.0.
  30.  *
  31.  *             Bob Schumaker
  32.  *             The AMIX Corporation
  33.  *             1881 Landings Drive
  34.  *             Mountain View, CA 94043-0848
  35.  *
  36.  * Internet:    bob@markets.amix.com
  37.  * UUCP:        {sun, uunet, netcom}!markets!bob
  38.  * CIS:            72227,2103
  39.  * AOL:            BSchumaker
  40.  *
  41.  * This version of TransSkel is written for THINK C 6.0.1.
  42.  * THINK C is a trademark of:
  43.  *
  44.  *     Symantec Corporation
  45.  *     10201 Torre Avenue
  46.  *     Cupertino, CA 95014  USA
  47.  *
  48.  * Reference key:
  49.  * IM        Inside Macintosh
  50.  * TN        Macintosh Technical Notes
  51.  * MHIG        Macintosh Human Interface Guidelines
  52.  * HIN        Human Interface Notes
  53.  * PGMF        Programmer's Guide to MultiFinder (APDA)
  54.  * TPN        TransSkel Programmer's Notes
  55.  *
  56.  * Recent history is given below.  Earlier change history is in TSHistory.
  57.  * If you've been writing applications with an earlier release, READ THAT FILE!
  58.  *
  59.  * 15 May 94 Release 3.17
  60.  * - Changed references to QuickDraw globals so they're written in terms of the
  61.  * qd struct.  E.g., thePort -> qd.thePort, gray -> qd.gray.
  62.  * - Redid some pattern references so they'll compile whether or not
  63.  * dangerousPattern is defined, and if universal headers are used.
  64.  * 25 May 94
  65.  * - Try to cast the argument to InitDialogs() properly depending on whether or
  66.  * not the universal headers are used.
  67.  *
  68.  * 10 Aug 94 Release 3.18
  69.  * - Began adding full support for Universal Headers so TransSkel can be
  70.  * compiled in native PowerPC mode.  I'm doing this under Metrowerks C, so
  71.  * for the moment this also involves porting to Metrowerks.  (For compiling
  72.  * M680x0 code under Metrowerks, no changes were necessary from release 3.17,
  73.  * which was a pleasant surprise.)
  74.  * - skelUnivHeaders invented.  If value is 1, the Universal Headers are
  75.  * available in the header files(and thus UPP definitions are also available),
  76.  * otherwise workarounds are necessary for UPP types.
  77.  * - skelPPC invented.  If value is 1, PPC code is being compiled.  Simpler
  78.  * check than checking for powerc and __powerc.
  79.  * 05 Sep 94
  80.  * - Fixed bug in SkelDialog() with not checking whether the window property
  81.  * type was already attached to the handler.  That could happen when replacing
  82.  * the handler for an existing window, and trying to add the property when one
  83.  * is already there returns an error.
  84.  * 30 Oct 94
  85.  * - Release 3.17 went to the qd form of QuickDraw global access, but that
  86.  * broke THINK Pascal support since THINK Pascal doesn't define the qd struct.
  87.  * References to the QuickDraw globals are now made using SkelQD(global), which
  88.  * can be defined to use the qd or non-qd access forms.  The qd form is still
  89.  * the default, but the THINK Pascal version of the TransSkel library can be
  90.  * compiled easily by supplying a non-qd definition of SkelQD().
  91.  */
  92.  
  93.  
  94. # include    <Traps.h>
  95. # include    <GestaltEqu.h>
  96. # include    <EPPC.h>
  97.  
  98.  
  99. /*
  100.  * TransSkel.h contains defines, typedefs, and public function
  101.  * prototypes
  102.  */
  103.  
  104. # include    "TransSkel.h"
  105.  
  106.  
  107. /*
  108.  * New(TypeName) returns handle to new object, for any TypeName.
  109.  * If there is insufficient memory, the result is nil.
  110.  */
  111.  
  112. # define    New(type)    (type **) NewHandle ((Size) sizeof (type))
  113.  
  114.  
  115. /* -------------- */
  116. /* Internal types */
  117. /* -------------- */
  118.  
  119.  
  120. /*
  121.  * Private data types for window and menu handlers
  122.  */
  123.  
  124. typedef struct WHandler    WHandler, *WHPtr, **WHHandle;
  125.  
  126. struct WHandler
  127. {
  128.     WindowPtr                whWind;        /* window/dialog to handle */
  129.     SkelWindMouseProcPtr    whMouse;    /* mouse-click handler */
  130.     SkelWindKeyProcPtr        whKey;        /* key-click handler */
  131.     SkelWindUpdateProcPtr    whUpdate;    /* update handler */
  132.     SkelWindActivateProcPtr    whActivate;    /* activate event handler */
  133.     SkelWindCloseProcPtr    whClose;    /* close "event" handler */
  134.     SkelWindClobberProcPtr    whClobber;    /* window disposal proc */
  135.     SkelWindIdleProcPtr        whIdle;        /* main loop idle proc */
  136.     SkelWindZoomProcPtr        whZoom;        /* zoom proc */
  137.     SkelWindSelectProcPtr    whSelect;    /* item selection proc (dialog) */
  138.     ModalFilterProcPtr            whFilter;    /* event filter proc (dialog) */
  139.     Rect        whGrow;                    /* limits on window sizing */
  140.     Boolean        whSized;                /* true = window was resized */
  141.     Boolean        whFrontOnly;            /* idle only when window active */
  142.     short        whFlags;                /* various flags */
  143.     SkelWindPropHandle    whProperties;    /* property list */
  144.     WHHandle    whNext;                    /* next window handler */
  145. };
  146.  
  147. typedef struct MHandler    MHandler, *MHPtr, **MHHandle;
  148.  
  149. struct MHandler
  150. {
  151.     short                    mhID;        /* menu id */
  152.     SkelMenuSelectProcPtr    mhSelect;    /* item selection handler */
  153.     SkelMenuClobberProcPtr    mhClobber;    /* menu disposal proc */
  154.     Boolean        mhSubMenu;                /* whether submenu */
  155.     MHHandle    mhNext;                    /* next menu handler */
  156. };
  157.  
  158.  
  159. /* ------------------------------------------- */
  160. /* Prototypes for internal (private) functions */
  161. /* ------------------------------------------- */
  162.  
  163. static WHHandle GetWHandler (WindowPtr w);
  164. static void DetachWHandler (WHHandle wh);
  165.  
  166. static void RouteEvent (EventRecord *evt);
  167.  
  168. static void DoMenuCommand (long command);
  169. static void DoMenuHook (void);
  170.  
  171. static void DoMouse (WHHandle h, EventRecord *evt);
  172. static void DoKey (WHHandle h, char ch, unsigned char code, short mods);
  173. static void DoUpdate (EventRecord *evt);
  174. static void DoActivate (EventRecord *evt);
  175. static void DoClose (WHHandle h);
  176. static void DoClobber (WHHandle h);
  177. static void DoDlogEvt (DialogPtr dlog, EventRecord *evt);
  178. static Boolean DoDlogFilter (DialogPtr dlog, EventRecord *evt);
  179.  
  180. static void DoGrow (WHHandle h, Point startPt);
  181. static void DoZoom (WHHandle h, short partCode);
  182.  
  183.  
  184. /* ------------------ */
  185. /* Internal variables */
  186. /* ------------------ */
  187.  
  188.  
  189. /*
  190.  * Window and menu handler variables.
  191.  *
  192.  * whList and mhList are the lists of window and menu handlers.
  193.  * mhClobOnRmve is true if the menu handler disposal proc
  194.  * is to be called when a handler is removed.  It is temporarily set
  195.  * false when handlers are installed for menus that already
  196.  * have handlers - the old handler is removed WITHOUT calling the
  197.  * disposal proc.  The effect is to replace the handler for the menu
  198.  * without destroying the menu itself.
  199.  *
  200.  * dragRect determines the limits on window dragging.  It is set in
  201.  * SkelInit() to the bounding box of the desktop region inset by 4 pixels.
  202.  *
  203.  * growRect contains the default limits on window sizing.  It is set in
  204.  * SkelInit().  The lower limits on window sizing of 80 pixels both directions
  205.  * is sufficient to allow text windows room to draw a grow box and scroll
  206.  * bars without having the thumb and arrows overlap.  The upper limits are
  207.  * determined from the screen size. (Probably incorrectly for the case of > 1
  208.  * screen.)
  209.  * These default values may be changed if with SkelGrowBounds if they are
  210.  * not appropriate.
  211.  *
  212.  * zoomProc is the default zoom procedure to use if the window does not have
  213.  * one of its own.  zoomProc may be nil, in which case the default is to zoom
  214.  * to just about full window size.
  215.  *
  216.  * mhDrawBarOnRmve determines whether the menu bar is redrawn by
  217.  * SkelRmveMenu() after taking a menu out of the menu bar.  Normally
  218.  * it's true, but SkelClobber() sets it false temporarily to avoid
  219.  * flicker as each menu is removed.
  220.  */
  221.  
  222.  
  223. static WHHandle    whList = (WHHandle) nil;
  224. static Rect        dragRect;
  225. static Rect        growRect;
  226. static SkelWindZoomProcPtr    zoomProc = (SkelWindZoomProcPtr) nil;
  227.  
  228.  
  229. static MHHandle    mhList = (MHHandle) nil;
  230. static Boolean    mhClobOnRmve = true;
  231. static Boolean    mhDrawBarOnRmve = true;
  232.  
  233.  
  234. /*
  235.  * Miscellaneous
  236.  *
  237.  * - skelEnv contains SysEnvirons() information.
  238.  * - sysVersion contains the system software version.
  239.  * - hasGestalt is true if Gestalt() is supported.
  240.  * - has64KROM is true if the current machine has the 64K ROM.
  241.  * - hasGetWVariant is true if GetWVariant() is supported.
  242.  * - mBarHeight is menu bar height.  Window sizing, zooming and dragging
  243.  * code takes this into account.  Initialized in SkelInit(), which see
  244.  * for teeth-gnashing over such a simple thing.
  245.  * - doneFlag determines when SkelEventLoop() returns.  It is set by calling
  246.  * SkelStopEventLoop(), which is how the host requests a halt.
  247.  * - pIdle points to a background procedure, to be run during event
  248.  * processing.  Set it with SkelSetIdle().  If nil, there's no
  249.  * procedure.
  250.  * - pEvent points to an event-inspecting hook, to be run whenever an
  251.  * event occurs.  Set it with SkelSetEventHook().  If nil, there's no
  252.  * procedure.
  253.  * - eventMask controls the event types requested by GetNextEvent() or
  254.  * WaitNextEvent() in SkelEventLoop().
  255.  * - pMenuHook points to a procedure called whenever a menu selection is about
  256.  * to be executed.  nil if no hook.
  257.  * - diskInitPt is the location at which the disk initialization dialog
  258.  * appears, if an uninitialized disk is inserted.
  259.  * - eventModifiers is the value of the modifiers field of the current event.
  260.  * - eventPtr points to the current event (nil if none seen yet).
  261.  * - defInitParams contains the default SkelInit() parameters if caller passes
  262.  * nil.
  263.  */
  264.  
  265. static SysEnvRec    skelEnv;
  266. static long    sysVersion = 0;
  267. static Boolean    hasGestalt;
  268. static Boolean    has64KROM;
  269. static Boolean    hasGetWVariant;
  270. static short    mBarHeight;
  271. static short    doneFlag;
  272. static short    eventMask = everyEvent ^ keyUpMask;
  273. static short    eventModifiers = 0;
  274. static EventRecord    *eventPtr = (EventRecord *) nil;
  275. static Point    diskInitPt = { /* v = */ 120, /* h = */ 100 };
  276.  
  277. static SkelIdleProcPtr            pIdle = (SkelIdleProcPtr) nil;
  278. static SkelEventHookProcPtr        pEvent = (SkelEventHookProcPtr) nil;
  279. static SkelMenuHookProcPtr        pMenuHook = (SkelMenuHookProcPtr) nil;
  280.  
  281. static SkelInitParams    defInitParams =
  282. {
  283.     6,                            /* no. of times to call MoreMasters() */
  284.     (GrowZoneUPP) nil,            /* GrowZone proc */
  285.     (SkelResumeProcPtr) nil,    /* resume proc */
  286.     0L                            /* stack adjustment */
  287. };
  288.  
  289. /*
  290.  * Multitasking support stuff
  291.  *
  292.  * hasWNE is true if WaitNextEvent() is available.
  293.  *
  294.  * inForeground is true if application is running in foreground (not
  295.  * suspended).  Initially true, per PGMF 3-1.
  296.  *
  297.  * getFrontClicks indicates whether the application wants to receive
  298.  * content-area clicks that bring it to the foreground.
  299.  *
  300.  * fgWaitTime and bgWaitTime are WaitNextEvent() times for foreground and
  301.  * background states.
  302.  */
  303.  
  304. static Boolean    hasWNE;
  305. static Boolean    inForeground = true;
  306. static long    fgWaitTime = 6L;            /* 0.1 seconds */
  307. static long    bgWaitTime = 300L;            /* 5.0 seconds */
  308. static Boolean    getFrontClicks = false;
  309. static SkelSuspendResumeProcPtr    pSuspendResume = (SkelSuspendResumeProcPtr) nil;
  310. static SkelClipCvtProcPtr    pClipCvt = (SkelClipCvtProcPtr) nil;
  311.  
  312. static WindowPtr    oldWindow = (WindowPtr) nil;     
  313. static WHHandle        oldWHandler = (WHHandle) nil;
  314.  
  315. /*
  316.  * Apple Event support
  317.  */
  318.  
  319. static Boolean    hasAppleEvents = 0;
  320. static SkelAEHandlerProcPtr    pAEHandler = (SkelAEHandlerProcPtr) nil;
  321.  
  322.  
  323. /* --------------------------- */
  324. /* Initialization and shutdown */
  325. /* --------------------------- */
  326.  
  327. /*
  328.  * Initialize the various Macintosh Managers and lots of other stuff.
  329.  *
  330.  * FlushEvents does NOT toss disk insert events; this is so disks
  331.  * inserted while the application is starting up don't result
  332.  * in dead drives.
  333.  *
  334.  * initParams contains initialization parameters:
  335.  * - the number of times to call MoreMasters
  336.  * - the address of a grow zone procedure to call if memory allocation
  337.  * problems occur (nil if none to be used)
  338.  * - the address of a resume procedure to pass to InitDialogs()
  339.  * (nil if none is to be used)
  340.  * - amount to adjust the application stack size by (default 0; no adjustment)
  341.  *
  342.  * if initParams is nil, defaults are used.
  343.  */
  344.  
  345. pascal void
  346. SkelInit (SkelInitParamsPtr initParams)
  347. {
  348. EventRecord    dummyEvent;
  349. Handle        h;
  350. long        result;
  351. short        i;
  352.  
  353.     if (initParams == (SkelInitParams *) nil)
  354.         initParams = &defInitParams;
  355.  
  356.     if (initParams->skelGzProc != (GrowZoneUPP) nil)
  357.         SetGrowZone (initParams->skelGzProc);
  358.  
  359.     SetApplLimit (GetApplLimit () - initParams->skelStackAdjust);
  360.  
  361.     MaxApplZone ();
  362.  
  363.     for (i = 0; i < initParams->skelMoreMasters; i++)
  364.         MoreMasters ();
  365.  
  366.     FlushEvents (everyEvent - diskMask, 0 );
  367.     InitGraf (&SkelQD (thePort));
  368.     InitFonts ();
  369.     InitWindows ();
  370.     InitMenus ();
  371.     TEInit ();
  372. /*
  373.  * Cast argument according to whether or not universal headers are used
  374.  */
  375. # if skelUnivHeaders
  376.     InitDialogs ((long) initParams->skelResumeProc);
  377. # else
  378.     InitDialogs (initParams->skelResumeProc);
  379. # endif
  380.     InitCursor ();
  381.  
  382.     (void) SysEnvirons (1, &skelEnv);
  383.     
  384.     sysVersion = (long) skelEnv.systemVersion;
  385.  
  386.     has64KROM = (skelEnv.machineType == envMac || skelEnv.machineType == envXL);
  387.  
  388.     /*
  389.      * If 64K ROM machine, use hard-coded value of 20.  Otherwise use
  390.      * Script Manager routine GetMBarHeight().  (This assumes, just to be
  391.      * safe, that GetMBarHeight() glue doesn't return 20 on 64K ROM systems,
  392.      * which it very well may.  The low memory variable MBarHeight (0x0BAA)
  393.      * isn't used because it doesn't exist on 64K ROM machines (TN OV 4, p.7).
  394.      */
  395.  
  396.     mBarHeight = (has64KROM ? 20 : GetMBarHeight ());
  397.  
  398.     /*
  399.      * Determine whether WaitNextEvent() is implemented (TN's OV 16 and TN TB 14)
  400.      */
  401.  
  402.     if (has64KROM)
  403.         hasWNE = false;
  404.     else
  405.         hasWNE = SkelTrapAvailable (_WaitNextEvent);
  406.  
  407.     hasGestalt = SkelTrapAvailable (_Gestalt);
  408.     hasAppleEvents = hasGestalt
  409.                     && Gestalt (gestaltAppleEventsAttr, &result) == noErr
  410.                     && (result & (1 << gestaltAppleEventsPresent));
  411.  
  412.     /*
  413.      * Determine whether GetWVariant() exists for checking whether a dialog is
  414.      * a movable modal or not.  The variant code can be gotten other ways, but
  415.      * the existence of trap precedes the existence of movalable modal windows,
  416.      * so if the trap doesn't exist, movable modals aren't likely to, either.
  417.      */
  418.  
  419.     hasGetWVariant = SkelTrapAvailable (_GetWVariant);
  420.  
  421.     /*
  422.      * Check whether application wants to get "bring to front" clicks.
  423.      */
  424.  
  425.     if ((h = GetResource ('SIZE', -1)) != (Handle) nil)
  426.     {
  427.         getFrontClicks = (((**(short **) h) & 0x200) != 0);
  428.         ReleaseResource (h);
  429.     }
  430.  
  431.     /*
  432.      * Window dragging limits are determined from bounding box of desktop.
  433.      * Upper limits of window sizing are related to that.  Both can involve
  434.      * multiple monitors, and should allow for menu bar.  dragRect is inset
  435.      * so as to leave at least 4 pixels of window title bar visible in both
  436.      * directions (IM I-289).
  437.      *
  438.      * GetGrayRgn() bounding box gives desktop extents.  On 64K ROM
  439.      * machines, GetGrayRgn() might not be present; could use GrayRgn
  440.      * bounding box, but use screenBits.bounds - menu bar, to avoid
  441.      * low memory access.  The two should be equivalent.
  442.      */
  443.  
  444.     if (has64KROM)
  445.     {
  446.         dragRect = SkelQD (screenBits.bounds);
  447.         dragRect.top += mBarHeight;
  448.     }
  449.     else
  450.     {
  451.         /* GetGrayRgn () already takes menu bar into account */
  452.         dragRect = (**GetGrayRgn ()).rgnBBox;
  453.     }
  454.  
  455.     SetRect (&growRect, 80, 80,
  456.                 dragRect.right - dragRect.left,
  457.                 dragRect.bottom - dragRect.top);
  458.  
  459.     InsetRect (&dragRect, 4, 4);
  460.  
  461.     /* let application come to front in multitasking environment, TN TB 35, p.8 */
  462.  
  463.     (void) EventAvail (everyEvent, &dummyEvent);
  464.     (void) EventAvail (everyEvent, &dummyEvent);
  465.     (void) EventAvail (everyEvent, &dummyEvent);
  466.     (void) EventAvail (everyEvent, &dummyEvent);
  467. }
  468.  
  469.  
  470. /*
  471.  * Copy the default initialization parameters into the structure
  472.  * pointed to by initParams.
  473.  */
  474.  
  475. pascal void
  476. SkelGetInitParams (SkelInitParamsPtr initParams)
  477. {
  478.     *initParams = defInitParams;
  479. }
  480.  
  481.  
  482. /*
  483.  * Clobber all the menu, window and dialog handlers.  Tell SkelRmveMenu()
  484.  * not to redraw menu bar so it doesn't flicker as menus are removed,
  485.  * then redraw it manually.
  486.  *
  487.  * Before removing window handlers, hide all the windows.  Do this from
  488.  * back to front (more esthetic and speedier).  If a window belongs to a DA,
  489.  * close the DA.  (For early systems (e.g., 4.1), if you leave a DA open,
  490.  * the system crashes the next time you try to open that DA.)
  491.  */
  492.  
  493. pascal void
  494. SkelCleanup (void)
  495. {
  496. Boolean    oldFlag;
  497. short    theKind;
  498. WindowPeek    w;
  499. WindowPtr    lastVis;
  500.  
  501.     for (;;)
  502.     {
  503.         lastVis = (WindowPtr) nil;
  504.         for (w = (WindowPeek) FrontWindow (); w != (WindowPeek) nil; w = w->nextWindow)
  505.         {
  506.             if (w->visible)
  507.                 lastVis = (WindowPtr) w;
  508.         }
  509.         if (lastVis == (WindowPtr) nil)        /* no more visible windows */
  510.             break;
  511.         if (lastVis != (WindowPtr) nil)
  512.         {
  513.             theKind = ((WindowPeek) lastVis)->windowKind;
  514.             if (theKind < 0)                /* DA, close it */
  515.                 CloseDeskAcc (theKind);
  516.             else
  517.                 HideWindow (lastVis);
  518.         }
  519.     }
  520.  
  521.     while (whList != (WHHandle) nil)
  522.         SkelRmveWind ((**whList).whWind);
  523.  
  524.     oldFlag = mhDrawBarOnRmve;
  525.     mhDrawBarOnRmve = false;
  526.     while (mhList != (MHHandle) nil)
  527.         SkelRmveMenu (GetMHandle((**mhList).mhID));
  528.     mhDrawBarOnRmve = oldFlag;
  529.     DrawMenuBar ();
  530. }
  531.  
  532.  
  533. /* ----------------------------------- */
  534. /* Execution environment interrogation */
  535. /* ----------------------------------- */
  536.  
  537.  
  538.  
  539. #define trapMask    0x0800
  540.  
  541. static short
  542. NumToolboxTraps (void)
  543. {
  544.     if (NGetTrapAddress (_InitGraf, ToolTrap)
  545.         == NGetTrapAddress (0xaa6e, ToolTrap))
  546.         return (0x200);
  547.     return (0x400);
  548. }
  549.  
  550.  
  551. static TrapType
  552. GetTrapType (short theTrap)
  553. {
  554.     return ((theTrap & trapMask) ? ToolTrap : OSTrap);
  555. }
  556.  
  557.  
  558. pascal Boolean
  559. SkelTrapAvailable (short theTrap)
  560. {
  561. TrapType    tType;
  562.  
  563.     if ((tType = GetTrapType (theTrap)) == ToolTrap)
  564.     {
  565.         theTrap &= 0x07ff;
  566.         if (theTrap >= NumToolboxTraps ())
  567.             theTrap = _Unimplemented;
  568.     }
  569.     return (NGetTrapAddress (theTrap, tType)
  570.                 != NGetTrapAddress (_Unimplemented, ToolTrap));
  571. }
  572.  
  573.  
  574. /*
  575.  * Query the TransSkel execution environment.  Shouldn't be called until
  576.  * after SkelInit() has been called.  Result is undefined if selector isn't
  577.  * legal.
  578.  */
  579.  
  580. pascal long
  581. SkelQuery (short selector)
  582. {
  583. long    result;
  584. Rect    r;
  585. RgnHandle    rgn;
  586.  
  587.     switch (selector)
  588.     {
  589.     case skelQVersion:
  590.         result = ((long) skelMajorRelease << 16) | skelMinorRelease;
  591.         break;
  592.     case skelQSysVersion:
  593.         result = sysVersion;
  594.         break;
  595.     case skelQHasWNE:
  596.         result = hasWNE ? 1 : 0;
  597.         break;
  598.     case skelQHas64KROM:
  599.         result = has64KROM ? 1 : 0;
  600.         break;
  601.     case skelQMBarHeight:
  602.         result = mBarHeight;
  603.         break;
  604.     case skelQHasColorQD:
  605.         result = skelEnv.hasColorQD ? 1 : 0;
  606.         break;
  607.     case skelQQDVersion:
  608.         /* get QuickDraw version number */
  609.         if (!hasGestalt
  610.             || Gestalt (gestaltQuickdrawVersion, &result) != noErr)
  611.             result = 0;                    /* assume original QuickDraw */
  612.         break;
  613.     case skelQInForeground:
  614.         result = inForeground ? 1 : 0;
  615.         break;
  616.     case skelQHasGestalt:
  617.         result = hasGestalt ? 1 : 0;
  618.         break;
  619.     case skelQHasAppleEvents:
  620.         result = hasAppleEvents ? 1 : 0;
  621.         break;
  622.     case skelQGrayRgn:
  623.         rgn = NewRgn ();
  624.         if (rgn != (RgnHandle) nil)
  625.         {
  626.             if (has64KROM)
  627.             {
  628.                 r = SkelQD (screenBits.bounds);
  629.                 r.top += mBarHeight;
  630.                 RectRgn (rgn, &r);
  631.             }
  632.             else
  633.             {
  634.                 /* GetGrayRgn () already takes menu bar into account */
  635.                 CopyRgn (GetGrayRgn (), rgn);
  636.             }
  637.         }
  638.         result = (long) rgn;
  639.         break;
  640.     default:
  641.         /* result is undefined! */
  642.         break;
  643.     }
  644.     return (result);
  645. }
  646.  
  647. /* ------------------------------------- */
  648. /* Event loop initiation and termination */
  649. /* ------------------------------------- */
  650.  
  651.  
  652. /*
  653.  * Main event loop.
  654.  *
  655.  * - Take care of DA's with SystemTask() if necessary.
  656.  * - Get an event.
  657.  * - Pass event to event router.
  658.  *    
  659.  * doneFlag is restored to its previous value upon exit.  This allows
  660.  * SkelEventLoop() to be called recursively.
  661.  */
  662.  
  663. pascal void
  664. SkelEventLoop (void)
  665. {
  666. EventRecord    evt;
  667. Boolean        oldDoneFlag;
  668. long        waitTime;
  669.  
  670.     oldDoneFlag = doneFlag;        /* save in case this is a recursive call */
  671.     doneFlag = false;            /* set for this call */
  672.     while (!doneFlag)
  673.     {
  674.         if (hasWNE)
  675.         {
  676.             waitTime = (inForeground ? fgWaitTime : bgWaitTime);
  677.             (void) WaitNextEvent (eventMask, &evt, waitTime, nil);
  678.         }
  679.         else
  680.         {
  681.             /*
  682.              * On some early versions of the system software, it cannot
  683.              * be assumed that the event contains a null event if the
  684.              * GetNextEvent() return value is false.  GetNextEvent() calls
  685.              * SystemEvent() to handle some DA events, and returns false
  686.              * if the event was handled.  However, in such cases the event
  687.              * record may still have the event that occurred, *not* a null
  688.              * event.  To avoid problems later with misinterpreting the
  689.              * event as non-null, force it to look like a null event.
  690.              */
  691.             SystemTask ();
  692.             if (!GetNextEvent (eventMask, &evt))
  693.                 evt.what = nullEvent;
  694.         }
  695.  
  696.             SkelRouteEvent (&evt);
  697.     }
  698.     doneFlag = oldDoneFlag;    /* restore in case this was a recursive call */
  699. }
  700.  
  701.  
  702. /*
  703.  * Tell current instance of SkelEventLoop() to drop dead
  704.  */
  705.  
  706. pascal void
  707. SkelStopEventLoop (void)
  708. {
  709.     doneFlag = true;
  710. }
  711.  
  712.  
  713. /* ----------------- */
  714. /* Event dispatching */
  715. /* ----------------- */
  716.  
  717.  
  718. /*
  719.  * Route a single event and run window idle procedures.
  720.  *
  721.  * If the event is a null-event, call the "no-event" handler for the front
  722.  * window and for any other windows with idle procedures that are always
  723.  * supposed to run.  This is done in such a way that it is safe for idle
  724.  * procs to remove the window handler for their own window if they want
  725.  * (unlikely, but...).
  726.  */
  727.  
  728. pascal void
  729. SkelRouteEvent (EventRecord *evt)
  730. {
  731. WHHandle    wh, wh2;
  732. GrafPtr        tmpPort;
  733. WindowPtr    w;
  734. SkelWindIdleProcPtr    p;
  735.  
  736.     RouteEvent (evt);
  737.  
  738.     /*
  739.      * Run applicable window idle procs.  Make sure to save and restore
  740.      * the port, since idle procs for the non-active window may be run.
  741.      */
  742.  
  743.     if (evt->what == nullEvent)
  744.     {
  745.         GetPort (&tmpPort);
  746.         for (wh = whList; wh != (WHHandle) nil; wh = wh2)
  747.         {
  748.             wh2 = (**wh).whNext;
  749.             w = (**wh).whWind;
  750.             if (w == FrontWindow () || !(**wh).whFrontOnly)
  751.             {
  752.                 if ((p = (**wh).whIdle) != (SkelWindIdleProcPtr) nil)
  753.                 {
  754.                     if (!hasWNE)
  755.                         SystemTask ();
  756.                     SetPort (w);
  757.                     (*p) ();
  758.                 }
  759.             }
  760.         }
  761.         SetPort (tmpPort);
  762.     }
  763. }
  764.  
  765.  
  766. /*
  767.  * General event dispatch routine.
  768.  *
  769.  * If there is an event-handling hook and it handles the event, the
  770.  * event is not further processed here.  Otherwise, run the application's idle
  771.  * time procedure if the event is a null event, then process the event.
  772.  *
  773.  * Null events are sent through DialogSelect() if a dialog is active.  This
  774.  * is necessary to make sure  the caret blinks if a dialog has any editText
  775.  * items.
  776.  *
  777.  * Network events are not supported as per the deprecation in TN NW 07.
  778.  * Application-defined events 1, 2 and 3 are not handled, either.
  779.  */
  780.  
  781. static void
  782. RouteEvent (EventRecord *evt)
  783. {
  784. Point        evtPt;
  785. GrafPtr        evtPort;
  786. short        evtPart;
  787. short        evtMods;
  788. char        evtChar;
  789. long        evtMsge;
  790. unsigned char evtCode;
  791. WHHandle    wh;
  792. WindowPtr    frontWind;
  793. Boolean        frontIsDlog;
  794. short        osMsge;
  795. Boolean        osResume;
  796. Boolean        osClipCvt;
  797. Rect        r1, r2;
  798. WStateData    **wdh;
  799. SignedByte    state;
  800.  
  801.     /* save values for SkelGetCurrentEvent() and SkelGetModifiers() */
  802.  
  803.     eventPtr = evt;
  804.     eventModifiers = evt->modifiers;
  805.  
  806.     /* don't bother handling event below if event hook does so here */
  807.     
  808.     if (pEvent != (SkelEventHookProcPtr) nil && (*pEvent) (evt))
  809.         return;
  810.  
  811.     frontWind = FrontWindow ();
  812.     frontIsDlog = SkelIsDlog (frontWind);
  813.  
  814.     evtPt = evt->where;
  815.     evtMods = evt->modifiers;
  816.     evtMsge = evt->message;
  817.  
  818.     switch (evt->what)
  819.     {
  820.     case nullEvent:
  821.         /*
  822.          * Run the application idle-time function.  If the front window is
  823.          * a dialog window, pass the event to the dialog event handler; this
  824.          * is necessary to make the caret blink if it has an edit text item.
  825.          * Don't use frontWind after calling the idle-time function, since
  826.          * the function might change the front window!
  827.          */
  828.         if (pIdle != (SkelIdleProcPtr) nil)
  829.             (*pIdle) ();
  830.         if (SkelIsDlog (FrontWindow ()))
  831.             DoDlogEvt (FrontWindow (), evt);
  832.         break;
  833.  
  834.     /*
  835.      * Mouse click.  Get the window in which the click occurred, and
  836.      * the part of the window.
  837.      */
  838.     case mouseDown:
  839.         evtPart = FindWindow (evtPt, &evtPort);
  840.         wh = GetWHandler (evtPort);
  841.  
  842.         /*
  843.          * Beep if a click occurs outside of a movable modal dialog box.
  844.          * Exceptions: allow clicks in menu bar, and command-clicks in
  845.          * drag region of underlying windows.
  846.          */
  847.  
  848.         if (SkelIsMMDlog (frontWind)
  849.             && !PtInRgn (evtPt, ((WindowPeek) frontWind)->strucRgn))
  850.         {
  851.             if (evtPart != inMenuBar
  852.                 && !(evtPart == inDrag && evtPort != frontWind && (evtMods & cmdKey)))
  853.             {
  854.                 SysBeep (1);
  855.                 break;
  856.             }
  857.         }
  858.  
  859.         switch (evtPart)
  860.         {
  861.  
  862.         /*
  863.          * Click in desk accessory window.  Pass back to the system.
  864.          */
  865.         case inSysWindow:
  866.             SystemClick (evt, evtPort);
  867.             break;
  868.  
  869.         /*
  870.          * Click in menu bar.  Track the mouse and execute
  871.          * selected command, if any.
  872.          */
  873.         case inMenuBar:
  874.             DoMenuHook ();
  875.             DoMenuCommand (MenuSelect (evtPt));
  876.             break;
  877.  
  878.         /*
  879.          * Click in grow box.  Resize window.
  880.          */
  881.         case inGrow:
  882.             DoGrow (wh, evtPt);
  883.             break;
  884.  
  885.         /*
  886.          * Click in title bar.  Drag the window around.
  887.          * Problem fix:  DragWindow() seems to call StillDown()
  888.          * first, so that clicks in drag regions while machine is
  889.          * busy don't otherwise bring window to front if the mouse
  890.          * is already up by the time DragWindow() is called.  So the
  891.          * window is selected first to make sure it's at least
  892.          * activated (unless the command key is down, IM I-289).
  893.          *
  894.          * Also offset the window's userState by the amount of the drag
  895.          * (it'd be simpler to set it to the final content rect but the
  896.          * window might be in zoomed state rather than user state).
  897.          */
  898.         case inDrag:
  899.             if (evtPort != frontWind && (evtMods & cmdKey) == 0)
  900.                 SelectWindow (evtPort);
  901.             SkelGetWindContentRect (evtPort, &r1);        /* post-drag position */
  902.             DragWindow (evtPort, evtPt, &dragRect);
  903.             SkelGetWindContentRect (evtPort, &r2);        /* post-drag position */
  904.             wdh = (WStateData **)(((WindowPeek) evtPort)->dataHandle);
  905.             state = HGetState ((Handle) wdh);
  906.             HLock ((Handle) wdh);
  907.             OffsetRect (&(**wdh).userState, r2.left - r1.left, r2.top - r1.top);
  908.             HSetState ((Handle) wdh, state);
  909.             break;
  910.  
  911.         /*
  912.          * Click in close box.  Call the close proc if the window
  913.          * has one.
  914.          */
  915.         case inGoAway:
  916.             if (TrackGoAway (evtPort, evtPt))
  917.                 DoClose (wh);
  918.             break;
  919.  
  920.         /*
  921.          * Click in zoom box.  Track the click and then zoom the
  922.          * window if necessary.
  923.          */
  924.         case inZoomIn:
  925.         case inZoomOut:
  926.             if (TrackBox (evtPort, evtPt, evtPart))
  927.                 DoZoom (wh, evtPart);
  928.             break;
  929.  
  930.         /*
  931.          * Click in content region.  If the window wasn't frontmost
  932.          * (active), just select it, otherwise pass the click to the
  933.          * window's mouse click handler.  Exception: if the application
  934.          * wants to receive content clicks event in non-frontmost windows,
  935.          * select the window and "repeat" the click.
  936.          */
  937.         case inContent:
  938.             if (evtPort != frontWind)
  939.             {
  940.                 SelectWindow (evtPort);
  941.                 if (!getFrontClicks)    /* don't pass click to handler */
  942.                     break;
  943.                 SetPort (evtPort);
  944.             }
  945.             if (frontIsDlog)
  946.                 DoDlogEvt (evtPort, evt);
  947.             else
  948.                 DoMouse (wh, evt);
  949.             break;
  950.  
  951.         }
  952.         break;    /* mouseDown */
  953.  
  954.     /*
  955.      * Key down event.  If the command key was down, process as menu
  956.      * item selection, otherwise pass the character and the modifiers
  957.      * flags to the active window's key handler.
  958.      *
  959.      * Command-period is not supposed to be used as a menu-item equivalent.
  960.      * Consequently, that's noticed as a special case and not passed to
  961.      * the menu routines.
  962.      */
  963.     case keyDown:
  964.     case autoKey:
  965.         evtChar = evtMsge & charCodeMask;
  966.         evtCode = (evtMsge & keyCodeMask) >> 8;    /* hope bit 7 isn't set! */
  967.  
  968.         if ((evtMods & cmdKey) && !SkelCmdPeriod (evt))    /* try menu equivalent */
  969.         {
  970.             DoMenuHook ();
  971.             DoMenuCommand (MenuKey (evtChar));
  972.             break;
  973.         }
  974.  
  975.         if (frontIsDlog)
  976.             DoDlogEvt (frontWind, evt);
  977.         else
  978.             DoKey (GetWHandler (frontWind), evtChar, evtCode, evtMods);
  979.         break;
  980.  
  981.     /*
  982.      * Key up event.  Key-ups are signified by setting the high bit
  983.      * of the key code.  This never executes unless the application
  984.      * changes the system event mask *and* the TransSkel event mask.
  985.      */
  986.     case keyUp:
  987.         evtChar = evtMsge & charCodeMask;            /* probably 0? */
  988.         evtCode = ((evtMsge & keyCodeMask) >> 8) | 0x80;
  989.  
  990.         if (frontIsDlog)
  991.             DoDlogEvt (frontWind, evt);
  992.         else
  993.             DoKey (GetWHandler (frontWind), evtChar, evtCode, evtMods);
  994.         break;
  995.  
  996.     /*
  997.      * Update a window.
  998.      */
  999.     case updateEvt:
  1000.         DoUpdate (evt);
  1001.         break;
  1002.  
  1003.     /*
  1004.      * Activate or deactivate a window.
  1005.      */
  1006.     case activateEvt:
  1007.         DoActivate (evt);
  1008.         break;
  1009.  
  1010.     /*
  1011.      * handle inserts of uninitialized disks.  Deactivate the frontmost
  1012.      * window since the disk-init dialog doesn't do anything with
  1013.      * activate events for other windows.
  1014.      */
  1015.     case diskEvt:
  1016.         if (HiWord (evtMsge) != noErr)
  1017.         {
  1018.             SkelActivate (FrontWindow (), false);
  1019.             DILoad ();
  1020.             (void) DIBadMount (diskInitPt, evtMsge);
  1021.             DIUnload ();
  1022.         }
  1023.         break;
  1024.  
  1025.     case osEvt:                /* aka app4Evt aka MultiFinder event */
  1026.         /* rip the message field into constituent parts */
  1027.         osMsge = ((evtMsge >> 24) & 0xff);            /* high byte */
  1028.         osResume = (Boolean) ((evtMsge & resumeFlag) != 0);
  1029.         osClipCvt = (Boolean) ((evtMsge & convertClipboardFlag) != 0);
  1030.  
  1031.         switch (osMsge)
  1032.         {
  1033.         case suspendResumeMessage:
  1034.             /*
  1035.              * Tell application it's being suspended or resumed
  1036.              * Tell application to convert scrap if necessary
  1037.              */
  1038.         
  1039.             inForeground = osResume;
  1040.             if (pSuspendResume != (SkelSuspendResumeProcPtr) nil)
  1041.                 (*pSuspendResume) (inForeground);
  1042.             if (!osResume)            /* always convert on suspend */
  1043.                 osClipCvt = true;
  1044.             if (osClipCvt && pClipCvt != (SkelClipCvtProcPtr) nil)
  1045.                 (*pClipCvt) (inForeground);
  1046.             break;
  1047.  
  1048.         case mouseMovedMessage:
  1049.             /* recompute mouse region -- not implemented */
  1050.             break;
  1051.  
  1052.         /*
  1053.          * 0xfd is a child-died event -- not implemented here since it's
  1054.          * only had limited use, e.g., by certain debuggers.  The child pid
  1055.          * is byte 2 ((evtMsge >> 16) & 0xff)
  1056.         case 0xfd:
  1057.             break;
  1058.          */
  1059.  
  1060.         default:                /* other OS event */
  1061.             /* pass event to catch-all handler -- not implemented */
  1062.             break;
  1063.         }
  1064.         break;
  1065.  
  1066.     case kHighLevelEvent:
  1067.         if (pAEHandler != (SkelAEHandlerProcPtr) nil)
  1068.             (*pAEHandler) (evt);
  1069.         break;
  1070.     }
  1071. }
  1072.  
  1073.  
  1074. /*
  1075.  * Activate or deactivate a window by synthesizing a fake
  1076.  * activate event and sending it through the event router.
  1077.  * Useful for activating a window when you don't know its
  1078.  * activate function.
  1079.  */
  1080.  
  1081. pascal void
  1082. SkelActivate (WindowPtr w, Boolean active)
  1083. {
  1084. EventRecord    evt;
  1085.  
  1086.     if (w != (WindowPtr) nil)
  1087.     {
  1088.         evt.what = activateEvt;
  1089.         evt.modifiers = active ? activeFlag : 0;
  1090.         evt.when = TickCount ();
  1091.         SetPt (&evt.where, 0, 0);
  1092.         evt.message = (long) w;
  1093.         SkelRouteEvent (&evt);
  1094.     }
  1095. }
  1096.  
  1097.  
  1098. /*
  1099.  * Call a window's close procedure.  Useful for closing a window when you
  1100.  * don't know its close function.
  1101.  *
  1102.  * This function knows how to close Desk Accessories.
  1103.  */
  1104.  
  1105. pascal void
  1106. SkelClose (WindowPtr w)
  1107. {
  1108.     if (w != (WindowPtr) nil)
  1109.     {
  1110.         if (((WindowPeek) w)->windowKind < 0)        /* DA window */
  1111.             CloseDeskAcc (((WindowPeek) w)->windowKind);
  1112.         else
  1113.             DoClose (GetWHandler (w));
  1114.     }
  1115. }
  1116.  
  1117.  
  1118. /*
  1119.  * Set the TransSkel event mask.  Does not have anything to do with the
  1120.  * system event mask.  See TPN 3.
  1121.  */
  1122.  
  1123. pascal void
  1124. SkelSetEventMask (short mask)
  1125. {
  1126.     eventMask = mask;
  1127. }
  1128.  
  1129.  
  1130. /*
  1131.  * Return the event mask.
  1132.  */
  1133.  
  1134. pascal short
  1135. SkelGetEventMask (void)
  1136. {
  1137.     return (eventMask);
  1138. }
  1139.  
  1140.  
  1141. /*
  1142.  * Install an idle-time task.  If p is nil, the current task is
  1143.  * disabled.
  1144.  */
  1145.  
  1146. pascal void
  1147. SkelSetIdle (SkelIdleProcPtr p)
  1148. {
  1149.     pIdle = p;
  1150. }
  1151.  
  1152.  
  1153. /*
  1154.  * Return the current idle-time task.  Return nil if none.
  1155.  */
  1156.  
  1157. pascal SkelIdleProcPtr
  1158. SkelGetIdle (void)
  1159. {
  1160.     return (pIdle);
  1161. }
  1162.  
  1163.  
  1164. /*
  1165.  * Install an event-inspecting hook.  If p is nil, the hook is
  1166.  * disabled.
  1167.  */
  1168.  
  1169. pascal void
  1170. SkelSetEventHook (SkelEventHookProcPtr p)
  1171. {
  1172.     pEvent = p;
  1173. }
  1174.  
  1175.  
  1176. /*
  1177.  * Return the current event-inspecting hook.  Return nil if none.
  1178.  */
  1179.  
  1180. pascal SkelEventHookProcPtr
  1181. SkelGetEventHook (void)
  1182. {
  1183.     return (pEvent);
  1184. }
  1185.  
  1186.  
  1187. pascal void
  1188. SkelSetSuspendResume (SkelSuspendResumeProcPtr p)
  1189. {
  1190.     pSuspendResume = p;
  1191. }
  1192.  
  1193.  
  1194. pascal SkelSuspendResumeProcPtr
  1195. SkelGetSuspendResume (void)
  1196. {
  1197.     return (pSuspendResume);
  1198. }
  1199.  
  1200.  
  1201. pascal void
  1202. SkelSetClipCvt (SkelClipCvtProcPtr p)
  1203. {
  1204.     pClipCvt = p;
  1205. }
  1206.  
  1207.  
  1208. pascal SkelClipCvtProcPtr
  1209. SkelGetClipCvt (void)
  1210. {
  1211.     return (pClipCvt);
  1212. }
  1213.  
  1214.  
  1215. pascal void
  1216. SkelSetWaitTimes (long fgTime, long bgTime)
  1217. {
  1218.     fgWaitTime = fgTime;
  1219.     bgWaitTime = bgTime;
  1220. }
  1221.  
  1222.  
  1223. pascal void
  1224. SkelGetWaitTimes (long *pFgTime, long *pBgTime)
  1225. {
  1226.     if (pFgTime != (long) nil)
  1227.         *pFgTime = fgWaitTime;
  1228.     if (pBgTime != (long) nil)
  1229.         *pBgTime = bgWaitTime;
  1230. }
  1231.  
  1232.  
  1233. pascal EventRecord *
  1234. SkelGetCurrentEvent (void)
  1235. {
  1236.     return (eventPtr);
  1237. }
  1238.  
  1239.  
  1240. pascal short
  1241. SkelGetModifiers (void)
  1242. {
  1243.     return (eventModifiers);
  1244. }
  1245.  
  1246.  
  1247. pascal void
  1248. SkelSetAEHandler (SkelAEHandlerProcPtr p)
  1249. {
  1250.     pAEHandler = p;
  1251. }
  1252.  
  1253.  
  1254. pascal SkelAEHandlerProcPtr
  1255. SkelGetAEHandler (void)
  1256. {
  1257.     return (pAEHandler);
  1258. }
  1259.  
  1260.  
  1261. /* -------------------------------------------------------------------- */
  1262. /*                    Window-handler event routing routines                */
  1263. /*                                                                        */
  1264. /*    See manual for discussion of port-setting behavior: the current        */
  1265. /*    port is set to a window when it becomes active in DoActivate().        */
  1266. /* -------------------------------------------------------------------- */
  1267.  
  1268.  
  1269. /*
  1270.  * Process dialog event.  dlog is the dialog to which the event applies.
  1271.  * Give the filter a chance at the event first.  If the filter doesn't
  1272.  * handle it, pass the event to DialogSelect().  If DialogSelect() selects
  1273.  * an item, pass the item to the window's item selection function, if
  1274.  * there is one.  This is used to dispose of dialog events that aren't
  1275.  * handled in some other more direct fashion.
  1276.  */
  1277.  
  1278.  
  1279. static void
  1280. DoDlogEvt (DialogPtr dlog, EventRecord *evt)
  1281. {
  1282. short        item;
  1283. WHHandle    wh;
  1284. SkelWindSelectProcPtr    select;
  1285.  
  1286.     if (DoDlogFilter (dlog, evt))
  1287.         return;
  1288.  
  1289.     if (DialogSelect (evt, &dlog, &item)
  1290.        && (wh = GetWHandler (dlog)) != (WHHandle) nil
  1291.        && (select = (**wh).whSelect) != (SkelWindSelectProcPtr) nil)
  1292.     {
  1293.         (*select) (dlog, item);
  1294.     }
  1295. }
  1296.  
  1297. /*
  1298.  * Run a dialog's filter function to give the filter first chance
  1299.  * at the event.
  1300.  *
  1301.  * The filter function returns false if it doesn't handle the event.
  1302.  * It returns true if it handled the event, in which case it should
  1303.  * set the item parameter.  The item will be passed to the dialog's
  1304.  * item selection function.
  1305.  *
  1306.  * If the filter function returns true, look up the handler again
  1307.  * just in case the filter function also called SkelRmveDlog().
  1308.  * If it did, the handler will have become invalid.  Looking it
  1309.  * up again avoids disaster.
  1310.  */
  1311.  
  1312. static Boolean
  1313. DoDlogFilter (DialogPtr dlog, EventRecord *evt)
  1314. {
  1315. short        item;
  1316. WHHandle    wh;
  1317. SkelWindSelectProcPtr    select;
  1318. ModalFilterProcPtr        filter;
  1319. Boolean    result = false;
  1320.  
  1321.     if ((wh = GetWHandler (dlog)) != (WHHandle) nil
  1322.         && (filter = (**wh).whFilter) != (ModalFilterProcPtr) nil)
  1323.     {
  1324.         if ((*(ModalFilterProcPtr) filter) (dlog, evt, &item))
  1325.         {
  1326.             if ((wh = GetWHandler (dlog)) != (WHHandle) nil
  1327.                 && (select = (**wh).whSelect) != (SkelWindSelectProcPtr) nil)
  1328.             {
  1329.                 (*select) (dlog, item);
  1330.             }
  1331.             result = true;
  1332.         }
  1333.     }
  1334.     return (result);
  1335. }
  1336.  
  1337.  
  1338. /*
  1339.  * Pass local mouse coordinates, click time, and the modifiers flag
  1340.  * word to the handler.  Should not be necessary to set the port, as
  1341.  * the click is passed to the active window's handler.
  1342.  */
  1343.  
  1344. static void
  1345. DoMouse (WHHandle h, EventRecord *evt)
  1346. {
  1347. Point    thePt;
  1348.  
  1349.     if (h != (WHHandle) nil && (**h).whMouse != (SkelWindMouseProcPtr) nil)
  1350.     {
  1351.         thePt = evt->where;    /* make local copy */
  1352.         GlobalToLocal (&thePt);
  1353.         (*(**h).whMouse) (thePt, evt->when, evt->modifiers);
  1354.     }
  1355. }
  1356.  
  1357.  
  1358. /*
  1359.  * Pass the character code, key code and the modifiers flag word to
  1360.  * the handler. Should not be necessary to set the port, as the click
  1361.  * is passed to the active window's handler.
  1362.  */
  1363.  
  1364. static void
  1365. DoKey (WHHandle h, char c, unsigned char code, short mods)
  1366. {
  1367.     if (h != (WHHandle) nil && (**h).whKey != (SkelWindKeyProcPtr) nil)
  1368.         (*(**h).whKey) ((short) c, (short) code, mods);
  1369. }
  1370.  
  1371.  
  1372. /*
  1373.  * Call the window updating procedure, passing to it an indicator whether
  1374.  * the window has been resized or not.  Then clear the flag, assuming
  1375.  * the update proc took whatever action was necessary to respond to
  1376.  * resizing.
  1377.  *
  1378.  * The Begin/EndUpdate stuff is done to clear the update region even if
  1379.  * the handler doesn't have any update proc.  Otherwise the Window
  1380.  * Manager will keep generating update events for the window, stalling  
  1381.  * updates of other windows.
  1382.  *
  1383.  * For dialog windows, UpdtDialog() does the normal item updating.  The
  1384.  * filter procedure can take care of non-item drawing, such as a bold
  1385.  * outline around a default button.
  1386.  *
  1387.  * Saves, sets, and restore the port, since it's not always the
  1388.  * active window that is updated.
  1389.  */
  1390.  
  1391. static void
  1392. DoUpdate (EventRecord *evt)
  1393. {
  1394. WHHandle    h;
  1395. GrafPtr    port;
  1396. GrafPtr    tmpPort;
  1397.  
  1398.     port = (WindowPtr) evt->message;
  1399.  
  1400.     GetPort (&tmpPort);
  1401.     SetPort (port);
  1402.     BeginUpdate (port);
  1403.     if (SkelIsDlog (port))
  1404.     {
  1405.         if (!DoDlogFilter (port, evt))
  1406.             UpdtDialog (port, port->visRgn);    /* let Dialog Manager finish update */
  1407.     }
  1408.     else
  1409.     {
  1410.         h = GetWHandler (port);
  1411.         if (h != (WHHandle) nil)
  1412.         {
  1413.             if ((**h).whUpdate != (SkelWindUpdateProcPtr) nil)
  1414.                 (*(**h).whUpdate) ((**h).whSized);
  1415.             (**h).whSized = false;
  1416.         }
  1417.     }
  1418.     EndUpdate (port);
  1419.     SetPort (tmpPort);
  1420. }
  1421.  
  1422.  
  1423. /*
  1424.  * Pass activate/deactivate notification to handler.  On activate,
  1425.  * set the port to the window coming active.  Normally this is done by
  1426.  * the user clicking in a window.
  1427.  *
  1428.  * *** BUT ***
  1429.  * Under certain conditions, a deactivate may be generated for a window
  1430.  * that has never had the port set to it by a preceding activate.  For
  1431.  * instance, if an application puts up window A, then window B in front
  1432.  * of A, then starts processing events, the first events will be a
  1433.  * deactivate for A and an activate for B.  Therefore, since it can't be
  1434.  * assumed that an activate ever set the port to A, the port needs to be
  1435.  * set for deactivates as well so drawing occurs in the correct port.
  1436.  *
  1437.  * This matters not a whit for the more usual cases that occur.  If a
  1438.  * deactivate for one window is followed by an activate for another, the
  1439.  * port will still be switched properly to the newly active window.  If
  1440.  * no activate follows the deactivate, the deactivated window is the last
  1441.  * one, and it doesn't matter what the port ends up set to, anyway.
  1442.  *
  1443.  * On deactivate, port is saved and restored in case deactivate is due to
  1444.  * a modal dialog having been brought in front and port changed to it
  1445.  * explicitly by the application.  The deactivate shouldn't leave the port
  1446.  * changed away from the dialog!
  1447.  *
  1448.  * For dialogs, DoDlogEvt() is called, allowing DialogSelect() to do
  1449.  * whatever it does for dialog activates.  The handler's activate procedure
  1450.  * is called in addition to this (e.g., to hilite controls or text selections,
  1451.  * adjust menus).
  1452.  */
  1453.  
  1454. static void
  1455. DoActivate (EventRecord *evt)
  1456. {
  1457. WHHandle    h;
  1458. GrafPtr    port;
  1459. GrafPtr    tmpPort;
  1460. Boolean    active;
  1461.  
  1462.     active = (evt->modifiers & activeFlag);
  1463.     port = (WindowPtr) evt->message;
  1464.  
  1465.  
  1466.     GetPort (&tmpPort);    /* save so can restore if deactivate */
  1467.     SetPort (port);
  1468.     if (SkelIsDlog (port))
  1469.         DoDlogEvt (port, evt);
  1470.     else
  1471.     {
  1472.         h = GetWHandler (port);
  1473.         if (h != (WHHandle) nil)
  1474.         {
  1475.             if ((**h).whActivate != (SkelWindActivateProcPtr) nil)
  1476.                 (*(**h).whActivate) (active);
  1477.         }
  1478.     }
  1479.     if (!active)
  1480.         SetPort (tmpPort);
  1481. }
  1482.  
  1483.  
  1484. /*
  1485.  * Execute a window handler's close box proc.  The close proc for
  1486.  * handlers for temp windows that want to remove themselves when the
  1487.  * window is closed can call SkelRmveWind to dispose of the window
  1488.  * and remove the handler from the window handler list.  Thus, windows
  1489.  * may be dynamically created and destroyed without filling up the
  1490.  * handler list with a bunch of invalid handlers.
  1491.  *
  1492.  * If the handler doesn't have a close proc, just hide the window.
  1493.  * The host should provide some way of reopening the window (perhaps
  1494.  * a menu selection).  Otherwise the window will be lost from user
  1495.  * control if it is hidden, since it won't receive user-initiated
  1496.  * events.
  1497.  *
  1498.  * This is called both for regular and dialog windows.
  1499.  *
  1500.  * Normally this is invoked because the close box of the active window
  1501.  * is clicked, in which case the port will be set to the window.  However,
  1502.  * SkelClose() allows the application to close an aritrary window, not just
  1503.  * the frontmost one -- so the port is saved and restored.
  1504.   */
  1505.  
  1506. static void
  1507. DoClose (WHHandle h)
  1508. {
  1509. GrafPtr    tmpPort;
  1510.  
  1511.     if (h != (WHHandle) nil)
  1512.     {
  1513.         GetPort (&tmpPort);
  1514.         SetPort ((**h).whWind);
  1515.         if ((**h).whClose != (SkelWindCloseProcPtr) nil)
  1516.             (*(**h).whClose) ();
  1517.         else
  1518.             HideWindow ((**h).whWind);
  1519.         SetPort (tmpPort);
  1520.     }
  1521. }
  1522.  
  1523.  
  1524. /*
  1525.  * Execute a window handler's clobber proc.  This is called both
  1526.  * for regular and dialog windows.
  1527.  *
  1528.  * Saves sets, and restores the port, since any window (not just active
  1529.  * one) may be clobbered at any time.
  1530.  *
  1531.  * Don't need to check whether handler is nil, as in other handler
  1532.  * procedures, since this is only called by SkelRmveWind with a
  1533.  * known-valid handler.
  1534.  */
  1535.  
  1536. static void
  1537. DoClobber (WHHandle h)
  1538. {
  1539. GrafPtr    tmpPort;
  1540.  
  1541.     GetPort (&tmpPort);
  1542.     SetPort ((**h).whWind);
  1543.     if ((**h).whClobber != (SkelWindClobberProcPtr) nil)
  1544.         (*(**h).whClobber) ();
  1545.     SetPort (tmpPort);
  1546. }
  1547.  
  1548.  
  1549. /*
  1550.  * Handlers for window events not requiring application handler routines
  1551.  * to be called.
  1552.  */
  1553.  
  1554.  
  1555. /*
  1556.  * Have either zoomed a window or sized it manually.  Invalidate
  1557.  * it to force an update and set the 'resized' flag in the window
  1558.  * handler true.  The port is assumed to be set to the port that changed
  1559.  * size.  Handler is assumed non-nil.
  1560.  */
  1561.  
  1562. static void
  1563. TriggerUpdate (WHHandle h)
  1564. {
  1565. GrafPtr    port = (**h).whWind;
  1566.  
  1567.     InvalRect (&port->portRect);
  1568.     (**h).whSized = true;
  1569. }
  1570.  
  1571.  
  1572. /*
  1573.  * Size a window, using the grow limits in the handler record.
  1574.  *
  1575.  * The portRect is invalidated to force an update event.  The window's
  1576.  * update handler procedure should check the parameter passed to it to
  1577.  * check whether the window has changed size, if it needs to adjust
  1578.  * itself to the new size.  THIS IS A CONVENTION.  Update procs must
  1579.  * notice grow "events", there is no procedure specifically for that.
  1580.  *
  1581.  * The clipping rectangle is not reset.  If the host application
  1582.  * keeps the clipping set equal to the portRect or something similar,
  1583.  * then it will have to arrange to treat window growing with more
  1584.  * care.
  1585.  *
  1586.  * Since the grow region of only the active window may be clicked,
  1587.  * it should not be necessary to set the port.
  1588.  */
  1589.  
  1590. static void
  1591. DoGrow (WHHandle h, Point startPt)
  1592. {
  1593. GrafPtr    growPort;
  1594. Rect    growRect;
  1595. long    growRes;
  1596.  
  1597.     if (h != (WHHandle) nil)
  1598.     {
  1599.         growPort = (**h).whWind;
  1600.         growRect = (**h).whGrow;
  1601.  
  1602.         /* growRes will be zero if the size was not actually changed */
  1603.     
  1604.         if (growRes = GrowWindow (growPort, startPt, &growRect))
  1605.         {
  1606.             SizeWindow (growPort, LoWord (growRes), HiWord (growRes), false);
  1607.             TriggerUpdate (h);
  1608.         }
  1609.     }
  1610. }
  1611.  
  1612.  
  1613. /*
  1614.  * Zoom the current window.  Very similar to DoGrow, but window
  1615.  * is erased before zooming for nicer visual effect (per IM IV-50,
  1616.  * TN TB 30, p.4).
  1617.  * 
  1618.  * Normally, since only the active window has a visible zoom box and
  1619.  * TransSkel sets the port to active window, this routine is triggered
  1620.  * by user-initiated clicks in zoom box and the port will be set to
  1621.  * the zoomed window.
  1622.  *
  1623.  * However, it is possible for zooms to be software initiated by the
  1624.  * application itself on any window; for such cases the port needs
  1625.  * to be saved and set before the zoom and restored afterward.
  1626.  */
  1627.  
  1628. static void
  1629. DoZoom (WHHandle h, short zoomDir)
  1630. {
  1631. GrafPtr    w;
  1632. GrafPtr    tmpPort;
  1633. Rect    r, growRect;
  1634.  
  1635.     if (h != (WHHandle) nil)
  1636.     {
  1637.         w = (**h).whWind;
  1638.         GetPort (&tmpPort);                    /* save port and set to */
  1639.         SetPort (w);                        /* zoomed window */
  1640.         if ((**h).whZoom != (SkelWindZoomProcPtr) nil)
  1641.             ((**h).whZoom) (w, zoomDir);    /* custom zoom proc */
  1642.         else if (zoomProc != (SkelWindZoomProcPtr) nil)
  1643.             (*zoomProc) (w, zoomDir);        /* custom default zoom proc */
  1644.         else                                /* default zooming */
  1645.         {
  1646.             EraseRect (&w->portRect);
  1647.             if (zoomDir == inZoomOut)    /* zooming to default state */
  1648.             {
  1649.                 /*
  1650.                  * Get the usable area of the device containing most of the
  1651.                  * window.  (Can ignore the result because the rect is always
  1652.                  * correct.  Pass nil for device parameter because it's
  1653.                  * irrelevant.)  Then adjust rect for title bar height, and
  1654.                  * inset it slightly.
  1655.                  */
  1656.                 (void) SkelGetWindowDevice (w, (GDHandle *) nil, &r);
  1657.                 r.top += SkelGetWindTitleHeight (w) - 1;
  1658.                 /* leave 3-pixel border */
  1659.                 InsetRect (&r, 3, 3);
  1660.                 /* clip to grow limits */
  1661.                 growRect = (**h).whGrow;
  1662.                 growRect.left = growRect.top = 0;
  1663.                 OffsetRect (&growRect, r.left, r.top);
  1664.                 SectRect (&r, &growRect, &r);
  1665.                 (**(WStateData **)(((WindowPeek)w)->dataHandle)).stdState = r;
  1666.             }
  1667.             ZoomWindow (w, zoomDir, false);
  1668.         }
  1669.         SetPort (tmpPort);        /* restore original port */
  1670.         TriggerUpdate (h);
  1671.     }
  1672. }
  1673.  
  1674.  
  1675. /* --------------------------------------------------------- */
  1676. /* Window handler installation/removal/modification routines */
  1677. /* --------------------------------------------------------- */
  1678.  
  1679.  
  1680. /*
  1681.  * Install handler for a window and set current port to it.  Remove
  1682.  * any previous handler for it.  Pass the following parameters:
  1683.  *
  1684.  * w
  1685.  *        Pointer to the window to be handled.  Must be created by host.
  1686.  * doMouse
  1687.  *        Proc to handle mouse clicks in window.  The proc will be
  1688.  *         passed the point (in local coordinates), the time of the
  1689.  *         click, and the modifier flags word.
  1690.  * doKey
  1691.  *        Proc to handle key clicks in window.  The proc will be passed
  1692.  *         the character and the modifier flags word.
  1693.  * doUpdate
  1694.  *        Proc for updating window.  TransSkel brackets calls to update
  1695.  *         procs with calls to BeginUpdate and EndUpdate, so the visRgn
  1696.  *         is set up correctly.  A flag is passed indicating whether the
  1697.  *         window was resized or not.  BY CONVENTION, the entire portRect
  1698.  *         is invalidated when the window is resized or zoomed.  That way,
  1699.  *         the handler's update proc can redraw the entire content region
  1700.  *         without interference from BeginUpdate/EndUpdate.  The flag
  1701.  *         is set to false after the update proc is called; the
  1702.  *         assumption is made that the proc will notice the resizing and
  1703.  *         respond appropriately.
  1704.  * doActivate
  1705.  *        Proc to execute when window is activated or deactivated.
  1706.  *         A boolean is passed to it which is true if the window is
  1707.  *         coming active, false if it's going inactive.
  1708.  * doClose
  1709.  *        Proc to execute when mouse clicked in close box.  Useful
  1710.  *         mainly to temp window handlers that want to know when to
  1711.  *         self-destruct (with SkelRmveWind).
  1712.  * doClobber
  1713.  *        Proc for disposal of handler's data structures
  1714.  * doWIdle
  1715.  *        Proc to execute when no events are pending.
  1716.  * idleFrontOnly
  1717.  *        True if doWIdle should execute on no events only when
  1718.  *         w is frontmost, false if executes all the time.  Note
  1719.  *         that if it always goes, everything else may be slowed down!
  1720.  *
  1721.  * If a particular procedure is not needed (e.g., key events are
  1722.  * not processed by a handler), pass nil in place of the appropriate
  1723.  * procedure address.
  1724.  *
  1725.  * Return true if successful, false if no handler could be allocated.
  1726.  * If false is returned, the port will not have been changed.
  1727.  */
  1728.  
  1729. pascal Boolean
  1730. SkelWindow (WindowPtr w,
  1731.             SkelWindMouseProcPtr doMouse,
  1732.             SkelWindKeyProcPtr doKey,
  1733.             SkelWindUpdateProcPtr doUpdate,
  1734.             SkelWindActivateProcPtr doActivate,
  1735.             SkelWindCloseProcPtr doClose,
  1736.             SkelWindClobberProcPtr doClobber,
  1737.             SkelWindIdleProcPtr doWIdle,
  1738.             Boolean idleFrontOnly)
  1739. {
  1740. WHHandle    whNew, whCur;
  1741. SkelWindPropHandle    wph = (SkelWindPropHandle) nil;
  1742.  
  1743.     /* Get new handler immediately, fail if can't allocate */
  1744.  
  1745.     if ((whNew = New (WHandler)) == (WHHandle) nil)
  1746.         return (false);
  1747.  
  1748.     /*
  1749.      * If there's a current handler for the window, remove it, but first
  1750.      * grab the property list from it so it can be transferred to the new
  1751.      * handler.
  1752.      */
  1753.     
  1754.     if ((whCur = GetWHandler (w)) != (WHHandle) nil)
  1755.     {
  1756.         wph = (**whCur).whProperties;
  1757.         (**whCur).whProperties = (SkelWindPropHandle) nil;
  1758.         DetachWHandler (whCur);
  1759.     }
  1760.  
  1761.     /*
  1762.      * Attach new handler to list of handlers.  It is attached to the
  1763.      * beginning of the list, which is simpler; the order is presumably
  1764.      * irrelevant to the host, anyway.
  1765.      *
  1766.      * Then fill in handler fields (including properties attached to any
  1767.      * previous handler).
  1768.      */
  1769.  
  1770.     (**whNew).whNext = whList;
  1771.     whList = whNew;
  1772.  
  1773.     (**whNew).whWind = w;
  1774.     (**whNew).whMouse = doMouse;
  1775.     (**whNew).whKey = doKey;
  1776.     (**whNew).whUpdate = doUpdate;
  1777.     (**whNew).whActivate = doActivate;
  1778.     (**whNew).whClose = doClose;
  1779.     (**whNew).whClobber = doClobber;
  1780.     (**whNew).whZoom = (SkelWindZoomProcPtr) nil;
  1781.     (**whNew).whIdle = doWIdle;
  1782.     (**whNew).whGrow = growRect;
  1783.     (**whNew).whSized = false;
  1784.     (**whNew).whFrontOnly = idleFrontOnly;
  1785.     (**whNew).whFlags = 0;
  1786.     (**whNew).whProperties = wph;
  1787.     (**whNew).whSelect = (SkelWindSelectProcPtr) nil;
  1788.     (**whNew).whFilter = (ModalFilterProcPtr) nil;
  1789.     SetPort (w);
  1790.  
  1791.     return (true);
  1792. }
  1793.  
  1794.  
  1795. /*
  1796.  * Remove a window handler.  This calls the handler's window disposal
  1797.  * routine and then takes the handler out of the handler list and
  1798.  * disposes of it (including its property list).
  1799.  *
  1800.  * SkelRmveWind is also called by SkelRmveDlog.
  1801.  */
  1802.  
  1803. pascal void
  1804. SkelRmveWind (WindowPtr w)
  1805. {
  1806. WHHandle    h;
  1807.  
  1808.     if ((h = GetWHandler (w)) == (WHHandle) nil)
  1809.         return;
  1810.  
  1811.     DoClobber (h);                                /* call disposal routine */
  1812.     SkelRmveWindProp (w, skelWPropAll);    /* toss properties */
  1813.  
  1814.     DetachWHandler (h);    /* remove handler for window from list */
  1815. }
  1816.  
  1817.  
  1818. /*
  1819.  * Install a handler for a modeless or movable modal dialog window and set
  1820.  * the port to it.  Remove any previous handler for it. SkelDialog calls
  1821.  * SkelWindow as a subsidiary to install a window handler, then sets
  1822.  * the event procedure on return.  A property is also added to the window
  1823.  * to indicate that it's a modeless or movable modal dialog.
  1824.  *
  1825.  * Pass the following parameters:
  1826.  *
  1827.  * dlog
  1828.  *        Pointer to the dialog to be handled.  Must be created by host.
  1829.  * doFilter
  1830.  *        Filter procedure to look at events before they are otherwise
  1831.  *        processed.
  1832.  * doSelect
  1833.  *        Procedure to execute when an item is "selected" (e.g., a mouse
  1834.  *        click occurs in it).
  1835.  * doClose
  1836.  *        Procedure to execute when mouse clicked in close box.  Useful
  1837.  *         mainly to dialog handlers that want to know when to
  1838.  *         self-destruct (with SkelRmveDlog).
  1839.  * doClobber
  1840.  *        Procedure for disposal of handler's data structures
  1841.  *
  1842.  * If a particular procedure is not needed, pass nil in place of
  1843.  * the appropriate procedure address.
  1844.  *
  1845.  * Return true if successful, false if no handler could be allocated.
  1846.  * If false is returned, the port will not have been changed.
  1847.  */
  1848.  
  1849. pascal Boolean
  1850. SkelDialog (DialogPtr dlog,
  1851.             ModalFilterProcPtr doFilter,
  1852.             SkelWindSelectProcPtr doSelect,
  1853.             SkelWindCloseProcPtr doClose,
  1854.             SkelWindClobberProcPtr doClobber)
  1855. {
  1856. WHHandle    wh;
  1857. short    propType;
  1858.  
  1859.     if (!SkelWindow (dlog, nil, nil, nil, nil, doClose, doClobber, nil, true))
  1860.         return (false);
  1861.  
  1862.     /*
  1863.      * Determine dialog property type to add to window and add the property,
  1864.      * unless it already has one.  (The property might already exist if
  1865.      * the handler for an existing window is being replaced.)
  1866.      */
  1867.     propType = (SkelIsMMDlog (dlog) ? skelWPropMovableModal : skelWPropModeless);
  1868.     if (SkelGetWindProp (dlog, propType) == (SkelWindPropHandle) nil
  1869.         && !SkelAddWindProp (dlog, propType, (long) 0))
  1870.     {
  1871.         SkelRmveDlog (dlog);
  1872.         return (false);
  1873.     }
  1874.  
  1875.     wh = GetWHandler (dlog);
  1876.     (**wh).whSelect = doSelect;
  1877.     (**wh).whFilter = doFilter;
  1878.     return (true);
  1879. }
  1880.  
  1881.  
  1882. /*
  1883.  * Remove a dialog and its handler
  1884.  */
  1885.  
  1886. pascal void
  1887. SkelRmveDlog (DialogPtr dlog)
  1888. {
  1889.     SkelRmveWind (dlog);
  1890. }
  1891.  
  1892.  
  1893. /*
  1894.  * Override the default sizing limits for a window, or, if w
  1895.  * is nil, reset the default limits used by SkelWindow.
  1896.  */
  1897.  
  1898. pascal void
  1899. SkelSetGrowBounds (WindowPtr w, short hLo, short vLo, short hHi, short vHi)
  1900. {
  1901. WHHandle    wh;
  1902. Rect        r;
  1903.  
  1904.     if (w == (WindowPtr) nil)
  1905.         SetRect (&growRect, hLo, vLo, hHi, vHi);
  1906.     else if ((wh = GetWHandler (w)) != (WHHandle) nil)
  1907.     {
  1908.         SetRect (&r, hLo, vLo, hHi, vHi);
  1909.         (**wh).whGrow = r;
  1910.     }
  1911. }
  1912.  
  1913.  
  1914. pascal void 
  1915. SkelSetZoom (WindowPtr w, SkelWindZoomProcPtr pZoom)
  1916. {
  1917. WHHandle    h;
  1918.  
  1919.     if (w == (WindowPtr) nil)
  1920.         zoomProc = pZoom;
  1921.     else if ((h = GetWHandler (w)) != (WHHandle) nil)
  1922.         (**h).whZoom = pZoom;
  1923. }
  1924.  
  1925.  
  1926. /*
  1927.  * Return zoom proc associated with window, nil if there isn't one.
  1928.  * Return default zoom proc if window is nil.
  1929.  */
  1930.  
  1931. pascal SkelWindZoomProcPtr
  1932. SkelGetZoom (WindowPtr w)
  1933. {
  1934. WHHandle    h;
  1935.  
  1936.     if (w == (WindowPtr) nil)
  1937.         return (zoomProc);
  1938.     if ((h = GetWHandler (w)) != (WHHandle) nil)
  1939.         return ((**h).whZoom);
  1940.     return ((SkelWindZoomProcPtr) nil);
  1941. }
  1942.  
  1943.  
  1944. pascal Boolean
  1945. SkelWindowRegistered (WindowPtr w)
  1946. {
  1947.     return ((Boolean) (GetWHandler (w) != (WHHandle) nil));
  1948. }
  1949.  
  1950.  
  1951. /*
  1952.  * Routines to determine whether a given window is a dialog, or a movable
  1953.  * modal dialog.  Safe to pass nil.
  1954.  */
  1955.  
  1956. pascal Boolean
  1957. SkelIsDlog (WindowPtr w)
  1958. {
  1959.     return (w != (WindowPtr) nil && ((WindowPeek)w)->windowKind == dialogKind);
  1960. }
  1961.  
  1962.  
  1963. pascal Boolean
  1964. SkelIsMMDlog (WindowPtr w)
  1965. {
  1966.     return (SkelIsDlog (w) && hasGetWVariant && GetWVariant (w) == movableDBoxProc);
  1967. }
  1968.  
  1969.  
  1970. /* ------------------------ */
  1971. /* Handler finders/removers */
  1972. /* ------------------------ */
  1973.  
  1974. /*
  1975.  * Get handler associated with a window.
  1976.  *
  1977.  * Return nil if window doesn't belong to any known handler.
  1978.  *
  1979.  * This routine is absolutely fundamental to TransSkel.
  1980.  */
  1981.  
  1982.  
  1983. static WHHandle
  1984. GetWHandler (WindowPtr w)
  1985. {
  1986. WHHandle    h;
  1987.  
  1988.     if (w == (WindowPtr) nil)
  1989.         return ((WHHandle) nil);
  1990.  
  1991.     if (w == oldWindow) 
  1992.         return (oldWHandler);        /* return handler of cached window */
  1993.  
  1994.     for (h = whList; h != (WHHandle) nil; h = (**h).whNext)
  1995.     {
  1996.         if ((**h).whWind == w)
  1997.         {
  1998.             oldWindow = w;    /* set cached window and handler */
  1999.             oldWHandler = h;
  2000.             return (h);
  2001.         }
  2002.     }
  2003.     return ((WHHandle) nil);
  2004. }
  2005.  
  2006.  
  2007. /*
  2008.  * Detach a handler from the handler list and destroy it.
  2009.  *
  2010.  * Clear window cache variable, just in case it points to the window
  2011.  * whose hander is being destroyed (and thus has become invalid).
  2012.  */
  2013.  
  2014. static void
  2015. DetachWHandler (WHHandle wh)
  2016. {
  2017. WHHandle    h, h2;
  2018.  
  2019.     if (whList != (WHHandle) nil)        /* if list empty, ignore */
  2020.     {
  2021.         if (whList == wh)                /* is it the first element? */
  2022.         {
  2023.             h2 = whList;
  2024.             whList = (**whList).whNext;
  2025.         }
  2026.         else for (h = whList; h != (WHHandle) nil; h = h2)
  2027.         {
  2028.             h2 = (**h).whNext;
  2029.             if (h2 == (WHHandle) nil)
  2030.                 return;                    /* handler not in list! (huh?) */
  2031.             if (h2 == wh)                /* found it */
  2032.             {
  2033.                 (**h).whNext = (**h2).whNext;
  2034.                 break;
  2035.             }
  2036.         }
  2037.         DisposeHandle ((Handle) h2);        /* get rid of handler record */
  2038.     }
  2039.  
  2040.     oldWindow = (WindowPtr) nil;    /* clear window cache variables */
  2041.     oldWHandler = (WHHandle) nil;
  2042. }
  2043.  
  2044.  
  2045. /* ------------------------------------------------------- */
  2046. /* Menu handler installation/removal/modification routines */
  2047. /* ------------------------------------------------------- */
  2048.  
  2049.  
  2050. /*
  2051.  * Install handler for a menu.  Remove any previous handler for it.
  2052.  * Pass the following parameters:
  2053.  *
  2054.  * theMenu
  2055.  *        Handle to the menu to be handled.  Must be created by host.
  2056.  * doSelect
  2057.  *        Proc that handles selection of items from menu.  If this is
  2058.  *         nil, the menu is installed, but nothing happens when items
  2059.  *         are selected from it.
  2060.  * doClobber
  2061.  *        Proc for disposal of handler's data structures.  Usually
  2062.  *         nil for menus that remain in menu bar until program
  2063.  *         termination.
  2064.  * subMenu
  2065.  *        True if the menu is a submenu (not installed in menu bar).
  2066.  * drawBar
  2067.  *        True if menu bar is to be drawn after menu is installed.
  2068.  *         (Ignored if the menu is a submenu.)
  2069.  *
  2070.  * Return true if successful, false if no handler could be allocated.
  2071.  */
  2072.  
  2073. pascal Boolean
  2074. SkelMenu (MenuHandle m,
  2075.             SkelMenuSelectProcPtr doSelect,
  2076.             SkelMenuClobberProcPtr doClobber,
  2077.             Boolean subMenu,
  2078.             Boolean drawBar)
  2079. {
  2080. MHHandle    mh;
  2081. Boolean        oldFlag;
  2082.  
  2083.     oldFlag = mhClobOnRmve;        /* remove any previous handler for */
  2084.     mhClobOnRmve = false;        /* menu, without redrawing menu bar */
  2085.     SkelRmveMenu (m);
  2086.     mhClobOnRmve = oldFlag;
  2087.  
  2088.     if ((mh = New (MHandler)) != (MHHandle) nil)
  2089.     {
  2090.         (**mh).mhNext = mhList;
  2091.         mhList = mh;
  2092.         (**mh).mhID = (**m).menuID;    /* get menu id number */
  2093.         (**mh).mhSelect = doSelect;            /* install selection handler */
  2094.         (**mh).mhClobber = doClobber;        /* install disposal handler */
  2095.         (**mh).mhSubMenu = subMenu;            /* set submenu flag */
  2096.         /* install menu in menu bar if not a submenu */
  2097.         InsertMenu (m, subMenu ? -1 : 0);
  2098.     }
  2099.     if (drawBar && !subMenu)
  2100.         DrawMenuBar ();
  2101.     return ((Boolean) (mh != (MHHandle) nil));
  2102. }
  2103.  
  2104.  
  2105. /*
  2106.  * Remove a menu handler.  This calls the handler's menu disposal
  2107.  * routine and then takes the handler out of the handler list and
  2108.  * disposes of it.  The menu bar is redrawn if the menu was not a
  2109.  * submenu and the global redraw flag hasn't been cleared.
  2110.  *
  2111.  * The menu MUST be deleted from the menu bar before calling the
  2112.  * clobber proc.  Otherwise the menu bar will end up filled with
  2113.  * garbage if the menu was allocated with NewMenu (IM I-352).
  2114.  */
  2115.  
  2116. pascal void
  2117. SkelRmveMenu (MenuHandle m)
  2118. {
  2119. short    mID;
  2120. MHHandle    h, h2;
  2121. SkelMenuClobberProcPtr    p;
  2122.  
  2123.     mID = (**m).menuID;
  2124.     if (mhList != (MHHandle) nil)            /* if list empty, ignore */
  2125.     {
  2126.         if ((**mhList).mhID == mID)    /* is it the first element? */
  2127.         {
  2128.             h2 = mhList;
  2129.             mhList = (**mhList).mhNext;
  2130.         }
  2131.         else
  2132.         {
  2133.             for (h = mhList; h != (MHHandle) nil; h = h2)
  2134.             {
  2135.                 h2 = (**h).mhNext;
  2136.                 if (h2 == (MHHandle) nil)
  2137.                     return;                        /* menu not in list! */
  2138.                 if ((**h2).mhID == mID)            /* found it */
  2139.                 {
  2140.                     (**h).mhNext = (**h2).mhNext;
  2141.                     break;
  2142.                 }
  2143.             }
  2144.         }
  2145.         DeleteMenu (mID);
  2146.         if (mhDrawBarOnRmve && !(**h2).mhSubMenu)
  2147.             DrawMenuBar ();
  2148.         if (mhClobOnRmve
  2149.             && (p = (**h2).mhClobber) != (SkelMenuClobberProcPtr) nil)
  2150.                 (*p) (m);                    /* call disposal routine */
  2151.         DisposeHandle ((Handle) h2);        /* get rid of handler record */
  2152.     }
  2153. }
  2154.  
  2155.  
  2156. /*
  2157.  * General menu-selection handler.  Just passes selection to the handler's
  2158.  * select routine.  If the select routine is nil, selecting items from
  2159.  * the menu is a nop.
  2160.  */
  2161.  
  2162. static void
  2163. DoMenuCommand (long command)
  2164. {
  2165. short        menu;
  2166. short        item;
  2167. MHHandle    mh;
  2168.  
  2169.     menu = HiWord (command);
  2170.     item = LoWord (command);
  2171.     for (mh = mhList; mh != (MHHandle) nil; mh = (**mh).mhNext)
  2172.     {
  2173.         if (menu == (**mh).mhID && (**mh).mhSelect != (SkelMenuSelectProcPtr) nil)
  2174.         {
  2175.             (*(**mh).mhSelect) (item);
  2176.             break;
  2177.         }
  2178.     }
  2179.     HiliteMenu (0);        /* command done, turn off menu hiliting */
  2180. }
  2181.  
  2182.  
  2183. /*
  2184.  * Menu is about to be pulled down or command-key executed.  Call menu
  2185.  * hook if there is one so application can set menus/items appropriately.
  2186.  */
  2187.  
  2188. static void
  2189. DoMenuHook (void)
  2190. {
  2191.     if (pMenuHook != (SkelMenuHookProcPtr) nil)
  2192.         (*pMenuHook) ();
  2193. }
  2194.  
  2195.  
  2196. pascal void
  2197. SkelSetMenuHook (SkelMenuHookProcPtr p)
  2198. {
  2199.     pMenuHook = p;
  2200. }
  2201.  
  2202.  
  2203. pascal SkelMenuHookProcPtr
  2204. SkelGetMenuHook (void)
  2205. {
  2206.     return (pMenuHook);
  2207. }
  2208.  
  2209.  
  2210. /* ------------------------ */
  2211. /* Window property routines */
  2212. /* ------------------------ */
  2213.  
  2214.  
  2215. /*
  2216.  * Add a property to a window.  Fail if the window is unregistered
  2217.  * or can't allocate memory for a new property structure.  If the
  2218.  * window already has such a property, fail.
  2219.  *
  2220.  * Returns a handle to the new property for success, nil for failure.
  2221.  */
  2222.  
  2223. pascal Boolean
  2224. SkelAddWindProp (WindowPtr w, short propType, long propData)
  2225. {
  2226. WHHandle        wh;
  2227. SkelWindPropHandle    ph;
  2228.  
  2229.     if (propType == skelWPropAll)
  2230.         return (false);
  2231.     if ((ph = SkelGetWindProp (w, propType)) != (SkelWindPropHandle) nil)
  2232.         return (false);
  2233.     /* if window is unregistered, or can't allocate structure, fail */
  2234.     if ((wh = GetWHandler (w)) == (WHHandle) nil
  2235.         || (ph = New (SkelWindProperty)) == (SkelWindPropHandle) nil)
  2236.         return (false);
  2237.     (**ph).skelWPropType = propType;
  2238.     (**ph).skelWPropData = propData;
  2239.     (**ph).skelWPropNext = (**wh).whProperties;
  2240.     (**wh).whProperties = ph;
  2241.     return (true);
  2242. }
  2243.  
  2244.  
  2245. /*
  2246.  * Remove a window property.  Does nothing if the window isn't
  2247.  * registered or if the window doesn't have the given property.
  2248.  *
  2249.  * If propType is skelWPropAll, SkelRmveWindProp() calls itself
  2250.  * recursively to remove all the properties on a window.  This
  2251.  * means that if you put skelWPropAll into the skelWPropType field
  2252.  * of a property, you'll get an infinite loop here.
  2253.  */
  2254.  
  2255. pascal void
  2256. SkelRmveWindProp (WindowPtr w, short propType)
  2257. {
  2258. WHHandle        wh;
  2259. SkelWindPropHandle    ph, ph2, pNext;
  2260.  
  2261.     if ((wh = GetWHandler (w)) == (WHHandle) nil
  2262.         || (ph = SkelGetWindProp (w, propType)) == (SkelWindPropHandle) nil)
  2263.         return;
  2264.  
  2265.     if (propType == skelWPropAll)    /* remove all properties */
  2266.     {
  2267.         while ((ph = (**wh).whProperties) != (SkelWindPropHandle) nil)
  2268.             SkelRmveWindProp (w, (**ph).skelWPropType);
  2269.         return;
  2270.     }
  2271.  
  2272.     /* remove particular property */
  2273.     if ((ph2 = (**wh).whProperties) == ph)    /* remove first in list */
  2274.         (**wh).whProperties = (**ph).skelWPropNext;
  2275.     else
  2276.     {
  2277.         while ((pNext = (**ph2).skelWPropNext) != (SkelWindPropHandle) nil)
  2278.         {
  2279.             if (pNext == ph)
  2280.             {
  2281.                 (**ph2).skelWPropNext = (**ph).skelWPropNext;
  2282.                 break;
  2283.             }
  2284.             ph2 = pNext;
  2285.         }
  2286.     }
  2287.     DisposeHandle ((Handle) ph);
  2288. }
  2289.  
  2290.  
  2291. /*
  2292.  * Find the given property for the window.  Fail if window is
  2293.  * unregistered or has no such property.
  2294.  */
  2295.  
  2296. pascal SkelWindPropHandle
  2297. SkelGetWindProp (WindowPtr w, short propType)
  2298. {
  2299. WHHandle            wh;
  2300. SkelWindPropHandle    ph = (SkelWindPropHandle) nil;
  2301.  
  2302.     if ((wh = GetWHandler (w)) != (WHHandle) nil)
  2303.     {
  2304.         if (propType == skelWPropAll)    /* return head of list */
  2305.             ph = (**wh).whProperties;
  2306.         else for (ph = (**wh).whProperties; ph != (SkelWindPropHandle) nil; ph = (**ph).skelWPropNext)
  2307.         {
  2308.             if ((**ph).skelWPropType == propType)
  2309.                 break;
  2310.         }
  2311.     }
  2312.     return (ph);
  2313. }
  2314.  
  2315.  
  2316. /*
  2317.  * Find the data value for a given property for the window.  Return 0 if window
  2318.  * is unregistered or has no such property.
  2319.  *
  2320.  * If you need to be able to distinquish an error return from a valid zero-value
  2321.  * data value, you should call SkelGetWindProp() instead, check for an error,
  2322.  * and extract the data value if there was no error.
  2323.  *
  2324.  * skelWPropAll is not a valid properly type for this call.
  2325.  */
  2326.  
  2327. pascal long
  2328. SkelGetWindPropData (WindowPtr w, short propType)
  2329. {
  2330. SkelWindPropHandle    ph;
  2331.  
  2332.     ph = SkelGetWindProp (w, propType);
  2333.     if (ph != (SkelWindPropHandle) nil)
  2334.         return ((**ph).skelWPropData);
  2335.     return (0);
  2336. }
  2337.